package de.lgohlke;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import de.lgohlke.concurrent.ThreadPool;

/**
 * <p>BenchmarkRunner class.</p>
 *
 * @author lars
 * @version $Id: $
 */
public class BenchmarkRunner
{
  private int                 loops   = 100;
  private final ThreadPool    runnerPool;
  private final Queue<String> results = new ConcurrentLinkedQueue<String>();

  private BenchmarkRunner(final ThreadPool pool)
  {
    runnerPool = pool;
  }

  /**
   * <p>createConcurrentBenchmarkRunner.</p>
   *
   * @param maxParallel a int.
   * @return a {@link de.lgohlke.BenchmarkRunner} object.
   */
  public static BenchmarkRunner createConcurrentBenchmarkRunner(final int maxParallel)
  {
    return new BenchmarkRunner(new ThreadPool(maxParallel));
  }

  /**
   * <p>createSerialBenchmarkRunner.</p>
   *
   * @return a {@link de.lgohlke.BenchmarkRunner} object.
   */
  public static BenchmarkRunner createSerialBenchmarkRunner()
  {
    return createConcurrentBenchmarkRunner(1);
  }

  /**
   * <p>createConcurrentBenchmarkRunner.</p>
   *
   * @return a {@link de.lgohlke.BenchmarkRunner} object.
   */
  public static BenchmarkRunner createConcurrentBenchmarkRunner()
  {
    return new BenchmarkRunner(ThreadPool.getInstance());
  }

  /**
   * <p>showStats.</p>
   */
  public void showStats()
  {
    runnerPool.waitForShutdown();

    System.out.println("max threadcount: " + runnerPool.getThreadPoolSize());
    for (String line : results)
    {
      System.out.println(line);
    }
  }

  /**
   * <p>enqueueJob.</p>
   *
   * @param job a {@link de.lgohlke.BenchmarkJob} object.
   */
  public void enqueueJob(final BenchmarkJob job)
  {
    runnerPool.submit(new Looper(getLoops()).runnableLoop(results, job));
  }

  /**
   * <p>Getter for the field <code>loops</code>.</p>
   *
   * @return a int.
   */
  public int getLoops()
  {
    return loops;
  }

  /**
   * <p>Setter for the field <code>loops</code>.</p>
   *
   * @param loops a int.
   */
  public void setLoops(final int loops)
  {
    this.loops = loops;
  }

  private static class Looper
  {
    private final int loops;

    public Looper(final int loops)
    {
      this.loops = loops;
    }

    private Runnable runnableLoop(final Queue<String> results, final BenchmarkJob job)
    {
      return new Runnable()
      {
        public void run()
        {
          String text = loop(job);
          results.add(String.format("%-30s \t", job.getName()) + text);
        }
      };
    }

    private String loop(final BenchmarkJob job)
    {
      long start = System.currentTimeMillis();

      for (int i = 0; i < loops; i++)
      {
        job.run();
      }

      long end = System.currentTimeMillis();

      long duration = (end - start);
      return String.format("%8.2f ms", duration / (float) 1000);
    }
  }
}
